/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package bean.session;

import entity.CompletedGoodsEntity;
import entity.CustomerAccountEntity;
import entity.CustomerEntity;
import entity.CustomerInvoiceEntity;
import entity.DepartmentEntity;
import entity.MonthlyRevenueEntity;
import entity.SalesOrderEntity;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import javax.annotation.Resource;
import javax.ejb.EJB;
import javax.ejb.SessionContext;
import javax.ejb.Stateless;
import javax.ejb.Timeout;
import javax.ejb.Timer;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import type.InvoiceStatus;
import type.InvoiceType;
import type.Month;
import type.SalesOrderStatus;
import util.Fixtures.CompletedGoods;
import util.Fixtures.Department;

/**
 *
 * @author wei
 */
@Stateless
public class CustomerInvoiceEntityFacade extends AbstractFacade<CustomerInvoiceEntity> {

    @PersistenceContext(unitName = "MacTeam-warPU")
    private EntityManager em;
    @Resource
    SessionContext ctx;
    @EJB
    private CustomerEntityFacade customerFacade;
    @EJB
    private SalesOrderEntityFacade salesOrderFacade;
    @EJB
    private InternalMessageEntityFacade messageFacade;
    @EJB
    private DepartmentEntityFacade departmentFacade;
    @EJB
    private CompletedGoodsEntityFacade goodsFacade;
    @EJB
    private MonthlyRevenueEntityFacade revenueFacade;

    protected EntityManager getEntityManager() {
        return em;
    }

    public CustomerInvoiceEntityFacade() {
        super(CustomerInvoiceEntity.class);
    }

    public void scheduleTimer(Date validity, long id) {
        ctx.getTimerService().createTimer(validity, id);
    }

    @Timeout
    public void timeoutHandler(Timer timer) {
        System.out.println("invoice expiry timer triggered");
        CustomerInvoiceEntity invoice = em.find(CustomerInvoiceEntity.class, timer.getInfo());
        timer.cancel();

        if (invoice == null || invoice.getStatus() == InvoiceStatus.paid) {
            return;
        }

        if (invoice.getType() == InvoiceType.normal) {
            //blacklist customer
            CustomerEntity customer = invoice.getCustomerAccount().getCustomer();
            customer.setBlackListed(true);
            customerFacade.edit(customer);

            //suspend current pending and confirmed sales orders
            List<SalesOrderEntity> saleOrders = salesOrderFacade.getPendingAndConfirmedSalesOrderForCustomer(customer.getId());
            String salesOrderIds = "";
            for (SalesOrderEntity so : saleOrders) {
                so.setBackupStatus(so.getStatus());
                so.setStatus(SalesOrderStatus.Suspended);

                salesOrderFacade.edit(so);
                salesOrderIds += so.getId() + ", ";
            }

            //generate invoice reminder
            invoice.setType(InvoiceType.reminder);
            invoice.setAmount(invoice.getAmount() + invoice.getLatePenalty());
            Calendar cal = Calendar.getInstance();
            cal.add(Calendar.DATE, 30);
            invoice.setDueDate(cal.getTime());
            invoice.setStatus(InvoiceStatus.draft);
            em.merge(invoice);

            //start a new timer on the reminder
            scheduleTimer(invoice.getDueDate(), invoice.getId());

            //inform sales about blacklist and suspension
            String blackListNotice = "Customer #" + customer.getId() + " "
                    + customer.getFullname() + " has been blacklisted. ";
            if (!salesOrderIds.isEmpty()) {
                blackListNotice += "Suspended ongoing sales order(s) " + salesOrderIds.substring(0, salesOrderIds.length() - 2);
            }
            messageFacade.createAndSendSystemMessage(customer.getCustomerExecutive(), blackListNotice, "/entities/salesOrderEntity/List.xhtml", null);

            //inform accounts receivable to post reminder
            String unpostedlink = "/entities/customerInvoiceEntity/ListUnposted.xhtml";
            DepartmentEntity ar = departmentFacade.getDepartmentByName(Department.AccountsReceivable.getName());
            messageFacade.createAndSendSystemMessage(ar.getManager(),
                    "Please post invoice reminder #" + invoice.getId(),
                    unpostedlink, null);

        } else if (invoice.getType() == InvoiceType.reminder) {
            invoice.setStatus(InvoiceStatus.defaulted);
            em.merge(invoice);
        }

    }

    public List<CustomerInvoiceEntity> getInvoiceByType(InvoiceType type) {
        Query query = em.createQuery("SELECT s FROM CustomerInvoice s "
                + " WHERE s.archived != 'true' AND s.type=:type");
        query.setParameter("type", type);

        return query.getResultList();
    }

    public List<CustomerInvoiceEntity> getAllCIA() {
        return getInvoiceByType(InvoiceType.cashInAdvance);
    }

    public List<CustomerInvoiceEntity> getAllNormal() {
        return getInvoiceByType(InvoiceType.normal);
    }

    public List<CustomerInvoiceEntity> getAllReminder() {
        return getInvoiceByType(InvoiceType.reminder);
    }

    public List<CustomerInvoiceEntity> getInvoiceByStatus(InvoiceStatus status) {
        Query query = em.createQuery("SELECT s FROM CustomerInvoice s "
                + " WHERE s.archived != 'true' AND s.status=:status");
        query.setParameter("status", status);

        return query.getResultList();
    }

    public List<CustomerInvoiceEntity> getAllUnposted() {
        return getInvoiceByStatus(InvoiceStatus.draft);
    }

    public List<CustomerInvoiceEntity> getAllUnpaid() {
        Query query = em.createQuery("SELECT s FROM CustomerInvoice s "
                + " WHERE s.archived != 'true' AND s.status!=:statusPaid AND s.status=:statusPosted");
        query.setParameter("statusPaid", InvoiceStatus.paid);
        query.setParameter("statusPosted", InvoiceStatus.posted);

        return query.getResultList();
    }

    public List<CustomerInvoiceEntity> getUnpaidRemindersForCustomer(CustomerEntity customer) {

        CustomerAccountEntity account = customer.getCustomerAccount();

        Query query = em.createQuery("SELECT s FROM CustomerInvoice s "
                + " WHERE s.archived != 'true' AND s.status!=:status AND s.type=:type"
                + " AND s.customerAccount=:account");
        query.setParameter("status", InvoiceStatus.paid);
        query.setParameter("type", InvoiceType.reminder);
        query.setParameter("account", account);

        return query.getResultList();
    }

    public List<CustomerInvoiceEntity> getInvoicesByYearMonth(int year, Month month) {
        try {
            String strDate = "01-" + month.getMonthNum() + "-" + year;
            DateFormat formatter = new SimpleDateFormat("dd-MM-yyyy");
            Date date = (Date) formatter.parse(strDate);
            Calendar cal = Calendar.getInstance();
            cal.setTime(date);

            Date after = cal.getTime();
            cal.add(Calendar.MONTH, 1);
            Date before = cal.getTime();

            Query query = em.createQuery("SELECT s FROM CustomerInvoice s "
                    + " WHERE s.archived != 'true' "
                    + " AND s.createdAt >= :after AND s.createdAt < :before");
            query.setParameter("after", after);
            query.setParameter("before", before);

            return query.getResultList();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return new ArrayList<CustomerInvoiceEntity>();
    }

    public List<MonthlyRevenueEntity> calculateMonthlyRevenueForYear(int yearOfCentery) {
        List<MonthlyRevenueEntity> monthlyRevenues = new ArrayList<MonthlyRevenueEntity>();
        for (Month m : Month.values()) {
            MonthlyRevenueEntity monthlyRev = new MonthlyRevenueEntity();
            monthlyRev.setYearOfCentury(yearOfCentery);
            monthlyRev.setMonthOfYear(m);

            calculateRevenue(monthlyRev);
            revenueFacade.create(monthlyRev);
            monthlyRevenues.add(monthlyRev);
        }
        return monthlyRevenues;
    }

    private void calculateRevenue(MonthlyRevenueEntity monthlyRev) {
        List<CustomerInvoiceEntity> invoices = getInvoicesByYearMonth(monthlyRev.getYearOfCentury(),
                monthlyRev.getMonthOfYear());
        double total = 0.0;
        double nutriNutsRevenue = 0.0;
        double nutriFruitsRevenue = 0.0;
        double powerChocRevenue = 0.0;
        double powerRedcurRevenue = 0.0;

        HashMap<CompletedGoodsEntity, Double> productToWorth = null;
        for (CustomerInvoiceEntity invoice : invoices) {
            if (invoice.getType() == InvoiceType.normal) {
                total += invoice.getAmount();
                productToWorth = invoice.productToWorth();
                nutriNutsRevenue += getFromHash(CompletedGoods.NutriNuts, productToWorth);
                nutriFruitsRevenue += getFromHash(CompletedGoods.NutriFruits, productToWorth);
                powerChocRevenue += getFromHash(CompletedGoods.PowerChoc, productToWorth);
                powerRedcurRevenue += getFromHash(CompletedGoods.powerRedcurr, productToWorth);
                
            } else if (invoice.getType() == InvoiceType.cashInAdvance) {
                total += invoice.getBillingRequest().getSalesOrder().getCashPayable();
            } else if (invoice.getType() == InvoiceType.reminder) {
                total += (invoice.getAmount() - invoice.getLatePenalty());
                productToWorth = invoice.productToWorth();
                nutriNutsRevenue += getFromHash(CompletedGoods.NutriNuts, productToWorth);
                nutriFruitsRevenue += getFromHash(CompletedGoods.NutriFruits, productToWorth);
                powerChocRevenue += getFromHash(CompletedGoods.PowerChoc, productToWorth);
                powerRedcurRevenue += getFromHash(CompletedGoods.powerRedcurr, productToWorth);
            }
        }
        monthlyRev.setRevenue(total);
        monthlyRev.setNutriNutsRevenue(nutriNutsRevenue);
        monthlyRev.setNutriFruitsRevenue(nutriFruitsRevenue);
        monthlyRev.setPowerChocRevenue(powerChocRevenue);
        monthlyRev.setPowerRedcurRevenue(powerRedcurRevenue);
    }
    
    private double getFromHash(CompletedGoods goods, HashMap<CompletedGoodsEntity, Double> productToWorth){
        CompletedGoodsEntity goodsEntity = goodsFacade.find(goods.getDbId());
        Double price = productToWorth.get(goodsEntity);
        return price==null?0.0:price;
    }
}
